package pr3.cruce;


import java.util.ArrayList;
import java.util.List;

import pr3.cromosoma.Cromosoma;

public abstract class EstrategiaCruce<E extends Cromosoma> {

    private final List<E> cruzados;

    public EstrategiaCruce() {
        cruzados = new ArrayList<E>();
    }

    public EstrategiaCruce(int size) {
        cruzados = new ArrayList<E>(size);
    }

    protected abstract void cruzar(E p1, E p2, E h1, E h2);

    public void cruce(List<E> seleccionado, double probCruce, List<E> hijos) {
        int indexHijos = seleccionarCruzados(seleccionado, probCruce, cruzados, hijos);

        int indexCruz = 0;
        while(indexCruz < cruzados.size()) {
            cruzar(
                    seleccionado.get(indexCruz),
                    seleccionado.get(indexCruz+1),
                    hijos.get(indexHijos),
                    hijos.get(indexHijos+1));
            indexCruz += 2;
            indexHijos += 2;
        }
        
    }

    /**
     * Dada una poblacion y una probabilidad de cruce, guarda en seleccionados los seleccionados
     * para cruzarse
     * @param poblacion la poblacion que va a cruzarse
     * @param probCruce la probabilidad de un individuo de cruzarse
     * @param cruzados la lista con los seleccionados para cruzarse (se garantiza que es par)
     * @param hijos aquellos que no han sido seleccionados para cruzarse
     * @return el numero de cromosomas que no han sido seleccionados
     */
    private int seleccionarCruzados(
            List<E> poblacion,
            double probCruce,
            List<E> cruzados,
            List<E> hijos) {

        int hijosIndex = 0;
        cruzados.clear();

		for (E cr : poblacion) {
			double prob = Math.random();

			if (prob < probCruce) {
				cruzados.add(cr);
			}
			else {
				hijos.get(hijosIndex).copia(cr);
                hijosIndex++;
			}
		}
        if(hijosIndex % 2 != 0) { //si se cruza un numero impar, eliminamos uno aleatorio
            int i = (int) (Math.random() * cruzados.size());
            E eliminado = cruzados.remove(i);
            hijos.get(hijosIndex).copia(eliminado);
            hijosIndex++;
        }
        return hijosIndex;
    }

}
